package com.anc.redis.redis;

import com.anc.redis.constants.RedisConstants;
import exception.AncException;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.util.Asserts;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author Anc
 * @date 2022/7/01
 */
@Component("redisClient")
public class RedisClientImpl implements RedisClient {

    /**
     * 默认后缀
     */
    private static final String KEY_SUFFIX = ":cached";
    /**
     * 默认前缀
     */
    private static final String DEFAULT_PREFIX = "hilife-payment:";

    @Resource
    private Environment environment;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public Object eval(String script, List<String> keys, List<String> args) {
        DefaultRedisScript<Integer> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        redisScript.setResultType(Integer.class);

        Object result = redisTemplate.execute((RedisCallback<Object>) redisConnection -> {
            Object nativeConnection = redisConnection.getNativeConnection();
            // 集群模式和单机模式虽然执行脚本的方法一样，但是没有共同的接口，所以只能分开执行
            // 集群模式
            if (nativeConnection instanceof JedisCluster) {
                return (Long) ((JedisCluster) nativeConnection).eval(script, keys, args);
            }

            // 单机模式
            else if (nativeConnection instanceof Jedis) {
                return (Long) ((Jedis) nativeConnection).eval(script, keys, args);
            }

            return -1L;
        });
        return result;
    }

    @Override
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    @Override
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    @Override
    public void set(String key, Object value, long expireTime, TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, expireTime, timeUnit);
    }

    @Override
    public Boolean setNX(String key, Object value) {
        return redisTemplate.opsForValue().setIfAbsent(key, value);
    }

    @Override
    public Boolean expire(String key, long time, TimeUnit timeUnit) {
        return redisTemplate.boundValueOps(key).expire(time, timeUnit);
    }

    @Override
    public Boolean persist(String key) {
        return redisTemplate.boundValueOps(key).persist();
    }

    @Override
    public Long increment(String key, long number) {
        return redisTemplate.opsForValue().increment(key, number);
    }

    @Override
    public Double increment(String key, double number) {
        return redisTemplate.opsForValue().increment(key, number);
    }

    @Override
    public Boolean delete(String key) {
        return redisTemplate.delete(key);
    }

    @Override
    public Long batchDeleteByLikeKey(String keys) {
        return null;
    }

//    @Override
//    public Long batchDeleteByLikeKey(String key) {
//        String realKey = key + "*";
//        Set<String> keys = redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
//            Set<String> binaryKeys = new HashSet<>();
//            Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(realKey).count(Integer.MAX_VALUE).build());
//            while (cursor.hasNext()) {
//                binaryKeys.add(new String(cursor.next()));
//            }
//            return binaryKeys;
//        });
//        return redisTemplate.delete(keys);
//    }

    @Override
    public void hset(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    @Override
    public void hsetAll(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    @Override
    public Boolean hsetNX(String key, String hashKey, Object value) {
        return redisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
    }

    @Override
    public Object hget(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }

    @Override
    public Map hgetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    @Override
    public String format(final String key) {
        Asserts.notBlank(key, "key");
        String applicationName = environment.getProperty("spring.application.name");
        if (StringUtils.isNotEmpty(applicationName)) {
            return applicationName + ":" + key + KEY_SUFFIX;
        }
        return DEFAULT_PREFIX + key + KEY_SUFFIX;
    }

    @Override
    public String format(final String key1, final String key2) {
        Asserts.notNull(key1, "redis key");
        Asserts.notNull(key2, "redis key");
        String applicationName = environment.getProperty("spring.application.name");
        if (StringUtils.isNotEmpty(applicationName)) {
            return applicationName + ":" + key1 + ":" + key2 + KEY_SUFFIX;
        }
        return DEFAULT_PREFIX + key1 + ":" + key2 + KEY_SUFFIX;
    }

    @Override
    public String format(String key1, String key2, String key3) {
        Asserts.notNull(key1, "redis key");
        Asserts.notNull(key2, "redis key");
        Asserts.notNull(key3, "redis key");
        String applicationName = environment.getProperty("spring.application.name");
        if (StringUtils.isNotEmpty(applicationName)) {
            return applicationName + ":" + key1 + ":" + key2 + ":" + key3 + KEY_SUFFIX;
        }
        return DEFAULT_PREFIX + key1 + ":" + key2 + ":" + key3 + KEY_SUFFIX;
    }

    @Override
    public String defaultFormatKey(String... keys) {
        if (keys.length == 0) {
            throw new AncException("参数个数不能为0");
        }
        String applicationName = environment.getProperty("spring.application.name");

        StringBuilder stringBuilder = new StringBuilder()
                .append(applicationName).append(RedisConstants.KEY_SEPARATOR);

        for (String arg : keys) {
            stringBuilder.append(arg).append(RedisConstants.KEY_SEPARATOR);
        }

        // 删除掉最后一位分隔符
        stringBuilder.deleteCharAt(stringBuilder.length() - 1);
        stringBuilder.append(KEY_SUFFIX);
        return stringBuilder.toString();
    }

    @Override
    public void setAdd(String key, String v) {
        redisTemplate.opsForSet().add(key, v);
    }

    @Override
    public String setPop(String key) {
        Object pop = redisTemplate.opsForSet().pop(key);
        if (null != pop) {
            return pop.toString();
        }
        return "";
    }

    @Override
    public Long setSize(String key) {
        return redisTemplate.opsForSet().size(key);
    }

}
